#include "Server.h"

/**
 * 构造方法。
 */
Server::Server() {
    // 连接信号和槽
    QTcpServer::connect(this, SIGNAL(newConnection()), SLOT(onAccept()));
}

/**
 * 析构方法。
 */
Server::~Server() {
    // 关闭服务
    QTcpServer::close();

    // 清空在线用户列表
    OnlineUsers::iterator end = mOnlineUsers.end();
    for (OnlineUsers::iterator i = mOnlineUsers.begin(); i != end; i++) {
        delete &i.value();
    }
    mOnlineUsers.clear();
}

/**
 * 处理寻找其它用户请求。
 * @param request   请求
 * @param client    客户端
 */
void Server::handleFindUsers(FindUsersRequest* request, Client* client) {
    // 创建结果
    FindUsersResult result;
    OnlineUsers::iterator end = mOnlineUsers.end();
    int i = 0;
    for (OnlineUsers::iterator it = mOnlineUsers.begin(); it != end; it++) {
        // 跳过自己的名字
        if (it.key() == request->mUsername) {
            continue;
        }
        strcpy_s(result.mUsernameList[i], GomokuProtocol::USERNAME_BUF_LEN, it.key().toStdString().c_str());
        i++;
    }

    // 发送结果
    this->sendData(client, &result, sizeof(result));
}

/**
 * 处理登录请求。
 * @param request   请求
 * @param client    客户端
 */
void Server::handleLogon(LogonRequest* request, Client* client) {
    // 如果已登录，则直接返回
    OnlineUsers::iterator it = mOnlineUsers.find(request->mUsername);
    if (it != mOnlineUsers.end()) {
        LogonResult result("");
        this->sendData(client, &result, sizeof(result));
        return;
    }

    // 保存用户
    mOnlineUsers.insert(request->mUsername, client);

    // 创建结果
    LogonResult result(request->mUsername);

    // 发送结果
    this->sendData(client, &result, sizeof(result));
}

/**
 * 处理协议。
 * @param pro       协议
 * @param client    客户端
 */
void Server::handleProtocol(GomokuProtocol* pro) {
    // 如果目标用户不在线则返回
    OnlineUsers::iterator it = mOnlineUsers.find(pro->mUsername);
    if (it == mOnlineUsers.end()) {
        return;
    }

    // 计算数据长度
    qint64 dataSize = -1;
    switch (pro->mType) {
        case GomokuProtocol::GAME_OVER:
            dataSize = sizeof(GameOver);
            break;
        case GomokuProtocol::INVITE_REQUEST:
            dataSize = sizeof(InviteRequest);
            break;
        case GomokuProtocol::INVITE_RESULT:
            dataSize = sizeof(InviteResult);
            break;
        case GomokuProtocol::PUT_PIECE:
            dataSize = sizeof(PutPiece);
            break;
        default:
            break;
    }

    // 发送给目标用户
    this->sendData(it.value(), pro, dataSize);
}

/**
 * 处理退出游戏请求。
 * @param request   请求
 * @param client    客户端
 */
void Server::handleQuitGame(QuitGame* request, Client* client) {
    // 广播该用户的请求
    OnlineUsers::iterator end = mOnlineUsers.end();
    for (OnlineUsers::iterator it = mOnlineUsers.begin(); it != end; it++) {
        this->sendData(it.value(), request, sizeof(QuitGame));
    }

    // 将该用户从在线列表移除
    mOnlineUsers.remove(request->mUsername);

    // 关闭客户端
    delete client;
}

/**
 * 客户端连接服务器事件的响应方法。
 */
void Server::onAccept() {
    QTcpSocket* clientSocket = QTcpServer::nextPendingConnection();
    new Client(clientSocket, this);
}

/**
 * 处理客户端发送来的数据。
 * @param pro       协议
 * @param client    客户端
 */
void Server::onGetMessageFromClient(GomokuProtocol* pro, Client* client) {
    switch (pro->mType) {
        case GomokuProtocol::FIND_USERS:
            this->handleFindUsers(reinterpret_cast<FindUsersRequest*>(pro), client);
            break;
        case GomokuProtocol::LOGON:
            this->handleLogon(reinterpret_cast<LogonRequest*>(pro), client);
            break;
        case GomokuProtocol::QUIT_GAME:
            this->handleQuitGame(reinterpret_cast<QuitGame*>(pro), client);
            break;
        default:
            this->handleProtocol(pro);
            break;
    }
}

/**
 * 发送数据。
 * @param client    客户端
 * @param pro       协议
 * @param dataSize  数据长度
 */
void Server::sendData(Client* client, GomokuProtocol* pro, qint64 dataSize) {
    // 发送数据长度
    client->write(&dataSize, sizeof(qint64));

    // 发送数据
    client->write(pro, dataSize);
}

/**
 * 开启服务。
 * @return 成功则返回 true，否则返回 false
 */
bool Server::start() {
    // 侦听
    QHostAddress serverAddr(GomokuProtocol::SERVER_IP);
    return QTcpServer::listen(serverAddr, GomokuProtocol::SERVER_PORT);
}
